﻿using System;
using System.Threading;
using Microsoft.Ccr.Core;
using NUnit.Framework;
using XcoAppSpaces.Dialogs;

namespace test.Dialogs
{
    [TestFixture]
    public class Test_Dialog
    {
        [Test]
        public void Create_a_dialog()
        {
            new Dialog().Start(new DispatcherQueue());

        }


        [Test]
        public void Add_message_handlers()
        {
            var d = new Dialog();
            d.Start();

            d.Handler<int>(i => Console.WriteLine("handling int"));
            d.Handler<string>(s => Console.WriteLine("handling string"));
        }


        [Test]
        public void Handle_messages()
        {
            var d = new Dialog();
            d.Start();

            var areInt = new AutoResetEvent(false);
            var areString = new AutoResetEvent(false);

            d.Handler<int>(i => areInt.Set());
            d.Handler<string>(s => areString.Set());

            d.Post(new DialogMessage(99, null));
            d.Post(new DialogMessage("hello", null));

            Assert.IsTrue(areInt.WaitOne(500));
            Assert.IsTrue(areString.WaitOne(500));
        }


        [Test]
        public void Handle_message_in_sync_context()
        {
            SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
            try
            {
                var d = new Dialog();
                d.Start();

                var areInt = new AutoResetEvent(false);

                d.Handler<int>(i => areInt.Set(), true);

                d.Post(new DialogMessage(99, null));

                Assert.IsTrue(areInt.WaitOne(500));
            }
            finally
            {
                SynchronizationContext.SetSynchronizationContext(null);
            }
        }


        [Test]
        public void Reply_to_message()
        {
            var d = new Dialog();
            d.Start();

            d.Handler<int>(i => d.Reply(i+1));

            var response = new Port<DialogMessage>();
            d.Post(new DialogMessage(99, response));

            var are = new AutoResetEvent(false);
            int reply = 0;
            Arbiter.Activate(
                new DispatcherQueue(),
                response.Receive(dm => { reply = (int)dm.Payload;
                                         are.Set(); })
                );

            Assert.IsTrue(are.WaitOne(500));
            Assert.AreEqual(100, reply);
        }


        [Test]
        public void Derive_custom_dialog_and_setup()
        {
            var d = new MyDialog();
            d.Start();

            d.Post(new DialogMessage(99, null));

            Assert.IsTrue(d.are.WaitOne(500));
        }

        [Test]
        public void Continue_handling_messages_after_exception()
        {
            var d = new MyDialog();
            d.Start();

            d.Post(new DialogMessage(-1, null));
            d.Post(new DialogMessage(99, null));
            Assert.IsTrue(d.are.WaitOne(500));
            Assert.IsTrue(d.exThrown);
        }


        [Test]
        public void Reply_with_timeout()
        {
            var d = new MyDialog();
            d.Start();

            var replyPort = new Port<DialogMessage>();
            Arbiter.Activate(
                new DispatcherQueue(),
                replyPort.Receive(
                    dm => Console.WriteLine("not replying to: {0}", dm.Payload)
                    )
                );
            d.Post(new DialogMessage("i wont answer a reply", replyPort));

            Assert.IsTrue(d.are.WaitOne(1000));
        }
    }


    class MyDialog : Dialog
    {
        public AutoResetEvent are = new AutoResetEvent(false);
        public bool exThrown = false;

        protected override void Define()
        {
            Handler<int>(i =>{
                                if (i < 0) 
                                {
                                    exThrown = true; 
                                    throw new ArgumentException(); 
                                }
                                are.Set();
                             });

            Handler<string>(s => {
                                    Console.WriteLine("received '{0}' and replying...", s);
                                    Reply("my reply",
                                          200,
                                          timeoutTimestamp =>
                                               {
                                                   Console.WriteLine("*** received reply timeout @ {0}", timeoutTimestamp);
                                                   are.Set();
                                               }
                                         );
                                });
        }
    }
}
